package com.ruoyi.system.service.impl;

import com.ruoyi.common.constant.JobGroupContants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.dto.SysWharfDto;
import com.ruoyi.common.core.domain.entity.system.*;
import com.ruoyi.common.core.domain.model.OperationTimeAndSwitchModeBody;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.handler.websocket.WebSocketMessageHandler;
import com.ruoyi.manager.WebSocketChannelManager;
import com.ruoyi.system.mapper.SysFenceMapper;
import com.ruoyi.system.mapper.SysScenicMapper;
import com.ruoyi.system.mapper.SysShiplineMapper;
import com.ruoyi.system.mapper.SysWharfMapper;
import com.ruoyi.system.service.ISysBoatService;
import com.ruoyi.system.service.ISysJobService;
import com.ruoyi.system.service.ISysScenicService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.service.TokenService;
import io.netty.channel.Channel;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 景区信息Service业务层处理
 *
 * @author ruoyi
 * @date 2023-05-04
 */
@Service
public class SysScenicServiceImpl implements ISysScenicService {
    @Resource
    private SysScenicMapper sysScenicMapper;

    @Resource
    private SysWharfMapper sysWharfMapper;

    @Resource
    private SysFenceMapper sysFenceMapper;

    @Resource
    private SysShiplineMapper sysShiplineMapper;

    @Autowired
    private ISysJobService sysJobService;

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private ISysBoatService sysBoatService;

    /**
     * 查询景区信息
     *
     * @param scenicId 景区信息主键
     * @return 景区信息
     */
    @Override
    public SysScenic selectSysScenicByScenicId(Long scenicId) {
        SysScenic scenic = sysScenicMapper.selectSysScenicById(scenicId);
        List<SysWharfDto> wharfList = sysWharfMapper.getWharfListByScenicId(scenicId);
        List<SysFence> fenceList = sysFenceMapper.getFenceListByScenicId(scenicId);
        List<SysShipline> shipLineList = sysShiplineMapper.getShiplineListByScenicId(scenicId);

        scenic.setWharfList(wharfList);
        scenic.setFenceList(fenceList);
        scenic.setShipLineList(shipLineList);

        return scenic;
    }

    /**
     * 查询景区信息列表
     *
     * @param sysScenic 景区信息
     * @return 景区信息
     */
    @Override
    public List<SysScenic> selectSysScenicList(SysScenic sysScenic) {
        List<SysScenic> scenicList = sysScenicMapper.selectSysScenicList(sysScenic).stream().map(scenic -> {
            List<SysWharfDto> wharfDtoList = sysWharfMapper.getWharfListByScenicId(scenic.getScenicId());
            scenic.setWharfList(wharfDtoList);
            scenic.setWharfNum(wharfDtoList.size());
            SysBoat sysBoat = new SysBoat();
            sysBoat.setScenicId(scenic.getScenicId());
            List<SysBoat> boatList = sysBoatService.selectSysBoatList(sysBoat);
            scenic.setBoatList(boatList);
            scenic.setBoatNum(boatList.size());
            return scenic;
        }).collect(Collectors.toList());
        return scenicList;
    }

    /**
     * 查询景区树结构信息
     *
     * @param sysScenic 景区信息
     * @return 景区树信息集合
     */
    @Override
    public List<TreeSelect> selectSysScenicTreeList(SysScenic sysScenic) {
//        List<SysScenic> scenics = SpringUtils.getAopProxy(this).selectSysScenicList(sysScenic);
        List<SysScenic> scenics = selectSysScenicList(sysScenic);
        return buildScenicTreeSelect(scenics);
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param depts 部门列表
     * @return 下拉树结构列表
     */
    @Override
    public List<TreeSelect> buildScenicTreeSelect(List<SysScenic> scenics) {
        List<SysScenic> scenicTrees = buildScenicTree(scenics);
        return scenicTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 构建前端所需要树结构
     *
     * @param scenics 部门列表
     * @return 树结构列表
     */
    @Override
    public List<SysScenic> buildScenicTree(List<SysScenic> scenics) {
        List<SysScenic> returnList = new ArrayList<>();
        List<Long> tempList = scenics.stream().map(SysScenic::getScenicId).collect(Collectors.toList());
        for (SysScenic scenic : scenics) {
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(scenic.getParentId())) {
                recursionFn(scenics, scenic);
                returnList.add(scenic);
            }
        }
        if (returnList.isEmpty()) {
            returnList = scenics;
        }
        return returnList;
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<SysScenic> list, SysScenic t) {
        // 得到子节点列表
        List<SysScenic> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysScenic tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysScenic> getChildList(List<SysScenic> list, SysScenic t) {
        List<SysScenic> tlist = new ArrayList<>();
        Iterator<SysScenic> it = list.iterator();
        while (it.hasNext()) {
            SysScenic n = it.next();
            if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getScenicId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysScenic> list, SysScenic t) {
        return getChildList(list, t).size() > 0;
    }

    /**
     * 新增景区信息
     *
     * @param sysScenic 景区信息
     * @return 结果
     */
    @Override
    public int insertSysScenic(SysScenic sysScenic) {
        Long parentId = sysScenic.getParentId();
        SysScenic parentScenic = sysScenicMapper.selectSysScenicById(parentId);
        // 如果父节点不为正常状态,则不允许新增子节点
        if (!UserConstants.SCENIC_NORMAL.equals(parentScenic.getStatus())) {
            throw new ServiceException("父景区已停用，不允许新增子景区");
        }
        sysScenic.setAncestors(parentScenic.getAncestors() + "," + parentId);
        return sysScenicMapper.insertSysScenic(sysScenic);
    }

    /**
     * 修改景区信息
     *
     * @param sysScenic 景区信息
     * @return 结果
     */
    @Override
    public int updateSysScenic(SysScenic sysScenic) {
        return sysScenicMapper.updateSysScenic(sysScenic);
    }

    /**
     * 批量删除景区信息
     *
     * @param scenicIds 需要删除的景区信息主键
     * @return 结果
     */
    @Override
    public int deleteSysScenicByScenicIds(Long[] scenicIds) {
        return sysScenicMapper.deleteSysScenicByScenicIds(scenicIds);
    }

    /**
     * 删除景区信息信息
     *
     * @param scenicId 景区信息主键
     * @return 结果
     */
    @Override
    public int deleteSysScenicByScenicId(Long scenicId) {
        return sysScenicMapper.deleteSysScenicByScenicId(scenicId);
    }

    @Override
    public boolean hasChildByScenicId(Long scenicId) {
        int result = sysScenicMapper.hasChildByScenicId(scenicId);
        return result > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOperationTimeAndSwitchMode(OperationTimeAndSwitchModeBody dataBody) throws SchedulerException, TaskException {
        String switchType = dataBody.getOperationStatusSwitchType();

        SysScenic scenic = new SysScenic();
        BeanUtils.copyProperties(dataBody, scenic);

        //更新景区的运营时间及运营状态切换模式。
        sysScenicMapper.updateSysScenic(scenic);

        SysJob sysJob1 = sysJobService.selectJobByCreatorAndGroup(SecurityUtils.getUsername(), JobGroupContants.START_OPERA_IN_WORKDAY);
        SysJob sysJob2 = sysJobService.selectJobByCreatorAndGroup(SecurityUtils.getUsername(), JobGroupContants.STOP_OPERA_IN_WORKDAY);
        SysJob sysJob3 = sysJobService.selectJobByCreatorAndGroup(SecurityUtils.getUsername(), JobGroupContants.START_OPERA_IN_WEEKDAY);
        SysJob sysJob4 = sysJobService.selectJobByCreatorAndGroup(SecurityUtils.getUsername(), JobGroupContants.STOP_OPERA_IN_WEEKDAY);

        if (Objects.equals(switchType, "0")) { //设置运营状态为自动切换
            if (Objects.nonNull(sysJob1)) {
                updateJob(sysJob1, dataBody, "0");
            } else {
                createAndAddJob(dataBody, scenic, JobGroupContants.START_OPERA_IN_WORKDAY);
            }

            if (Objects.nonNull(sysJob2)) {
                updateJob(sysJob2, dataBody, "0");
            } else {
                createAndAddJob(dataBody, scenic, JobGroupContants.STOP_OPERA_IN_WORKDAY);
            }

            if (Objects.nonNull(sysJob3)) {
                updateJob(sysJob3, dataBody, "0");
            } else {
                createAndAddJob(dataBody, scenic, JobGroupContants.START_OPERA_IN_WEEKDAY);
            }

            if (Objects.nonNull(sysJob4)) {
                updateJob(sysJob4, dataBody, "0");
            } else {
                createAndAddJob(dataBody, scenic, JobGroupContants.STOP_OPERA_IN_WEEKDAY);
            }
        } else {//设置运营状态为手动切换，此时需将相关定时任务的状态设置为停用
            if (Objects.nonNull(sysJob1)) {
                updateJob(sysJob1, dataBody, "1");
            }

            if (Objects.nonNull(sysJob2)) {
                updateJob(sysJob2, dataBody, "1");
            }

            if (Objects.nonNull(sysJob3)) {
                updateJob(sysJob3, dataBody, "1");
            }

            if (Objects.nonNull(sysJob4)) {
                updateJob(sysJob4, dataBody, "1");
            }
        }
    }

    /**
     * 修改景区的运营状态。
     *
     * @param
     * @return
     * @author ZJW
     * @createTime 2023/7/12 23:26
     */
    @Override
    public void changeOperationStatus(Long scenicId, String operationStatus) {
        int row = sysScenicMapper.changeOperationStatus(scenicId, operationStatus);
        if (row > 0) {
            Channel scenicWebSocketChannel = WebSocketChannelManager.getScenicWebSocketChannelByScenicId(scenicId);
            if (Objects.nonNull(scenicWebSocketChannel)) {
                WebSocketMessageHandler.sendScenicOperaStatusChangedMsg(scenicWebSocketChannel, scenicId, operationStatus);
            }
        }
    }

    private void updateJob(SysJob existSysJob, OperationTimeAndSwitchModeBody dataBody, String status) throws SchedulerException, TaskException {
        String jobGroup = existSysJob.getJobGroup();
        String cronStr = "";
        switch (jobGroup) {
            case JobGroupContants.START_OPERA_IN_WORKDAY:
                String[] arr1 = dataBody.getStartTimeOfWorkday().split(":");
                cronStr = arr1[2] + " " + arr1[1] + " " + arr1[0] + " " + "? * 2-6";
                break;
            case JobGroupContants.STOP_OPERA_IN_WORKDAY:
                String[] arr2 = dataBody.getEndTimeOfWorkday().split(":");
                cronStr = arr2[2] + " " + arr2[1] + " " + arr2[0] + " " + "? * 2-6";
                break;
            case JobGroupContants.START_OPERA_IN_WEEKDAY:
                String[] arr3 = dataBody.getStartTimeOfWeekday().split(":");
                cronStr = arr3[2] + " " + arr3[1] + " " + arr3[0] + " " + "? * 7,1";
                break;
            case JobGroupContants.STOP_OPERA_IN_WEEKDAY:
                String[] arr4 = dataBody.getEndTimeOfWeekday().split(":");
                cronStr = arr4[2] + " " + arr4[1] + " " + arr4[0] + " " + "? * 7,1";
                break;
        }

        existSysJob.setCronExpression(cronStr);
        existSysJob.setStatus(status);
        existSysJob.setUpdateBy(SecurityUtils.getUsername());
        existSysJob.setUpdateTime(new Date());

        sysJobService.updateJob(existSysJob);
    }

    private void createAndAddJob(OperationTimeAndSwitchModeBody dataBody, SysScenic scenic, String jobGroup) throws SchedulerException, TaskException {
        Long scenicId = scenic.getScenicId();
        StringBuilder jobName = new StringBuilder(scenic.getScenicShortName());
        String invokeTarget = "";
        String cronStr = "";
        switch (jobGroup) {
            case JobGroupContants.START_OPERA_IN_WORKDAY:
                jobName.append("-工作日开启运营");
                invokeTarget = String.format("wjTask.changeOperationStatus(%d,'%s')", scenicId, "0");
                String[] arr1 = dataBody.getStartTimeOfWorkday().split(":");
                cronStr = arr1[2] + " " + arr1[1] + " " + arr1[0] + " " + "? * 2-6";
                break;
            case JobGroupContants.STOP_OPERA_IN_WORKDAY:
                jobName.append("-工作日停止运营");
                invokeTarget = String.format("wjTask.changeOperationStatus(%d,'%s')", scenicId, "1");
                String[] arr2 = dataBody.getEndTimeOfWorkday().split(":");
                cronStr = arr2[2] + " " + arr2[1] + " " + arr2[0] + " " + "? * 2-6";
                break;
            case JobGroupContants.START_OPERA_IN_WEEKDAY:
                jobName.append("-非工作日开启运营");
                invokeTarget = String.format("wjTask.changeOperationStatus(%d,'%s')", scenicId, "0");
                String[] arr3 = dataBody.getStartTimeOfWeekday().split(":");
                cronStr = arr3[2] + " " + arr3[1] + " " + arr3[0] + " " + "? * 7,1";
                break;
            case JobGroupContants.STOP_OPERA_IN_WEEKDAY:
                jobName.append("-非工作日停止运营");
                invokeTarget = String.format("wjTask.changeOperationStatus(%d,'%s')", scenicId, "1");
                String[] arr4 = dataBody.getEndTimeOfWeekday().split(":");
                cronStr = arr4[2] + " " + arr4[1] + " " + arr4[0] + " " + "? * 7,1";
                break;
        }

        SysJob job = new SysJob();
        job.setCreateBy(SecurityUtils.getUsername());
        job.setCreateTime(new Date());
        job.setJobName(jobName.toString());
        job.setJobGroup(jobGroup);
        job.setInvokeTarget(invokeTarget);
        job.setCronExpression(cronStr);
        job.setMisfirePolicy("3"); //计划执行错误策略（1立即执行 2执行一次 3放弃执行）
        job.setConcurrent("1"); //是否并发执行（0允许 1禁止）
        job.setStatus("0"); //状态（0正常 1暂停）

        sysJobService.insertJob(job);
    }

    /**
     * 校验景区名称是否唯一
     *
     * @param dept 部门信息
     * @return 结果
     */
    @Override
    public String checkScenicNameUnique(SysScenic scenic) {
        Long scenicId = StringUtils.isNull(scenic.getScenicId()) ? -1L : scenic.getScenicId();
        SysScenic sysScenic = sysScenicMapper.checkScenicNameUnique(scenic.getScenicName(), scenic.getParentId());
        if (StringUtils.isNotNull(sysScenic) && sysScenic.getScenicId().longValue() != scenicId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }
}
